package com.itheima.behavior.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.itheima.book.service.BookService;
import com.itheima.behavior.config.KafkaConfig;
import com.itheima.behavior.service.RecommendService;
import com.itheima.behavior.strategy.ColdStartStrategy;
import com.itheima.common.redis.CacheService;
import com.itheima.model.behavior.dtos.RecommendQueryDto;
import com.itheima.model.behavior.pojos.UserBehavior;
import com.itheima.model.behavior.pojos.UserProfile;
import com.itheima.model.behavior.vos.RecommendItemVo;
import com.itheima.model.common.dtos.ResponseResult;
import com.itheima.utils.thread.MpThreadLocalUtil;
import com.itheima.model.user.pojos.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 推荐服务实现类
 */
@Service
@Slf4j
public class RecommendServiceImpl implements RecommendService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private BookService bookService;

    @Autowired
    private ColdStartStrategy coldStartStrategy;

    private static final int DEFAULT_LIMIT = 10;
    private static final int MAX_LIMIT = 50;
    private static final int COLD_START_BEHAVIOR_THRESHOLD = 5; // 冷启动阈值
    
    // 缓存过期时间
    private static final int CACHE_EXPIRE_SECONDS = 300; // 5分钟
    
    // 推荐多样性控制
    private static final double CATEGORY_DIVERSITY_THRESHOLD = 0.3; // 同一分类不超过30%
    private static final int MAX_SAME_CATEGORY_COUNT = 3; // 同一分类最多3本

    @Override
    public ResponseResult getPersonalizedRecommend(Integer limit) {
        try {
            User user = MpThreadLocalUtil.getUser();
            Integer userId = user != null ? user.getId() : null;
            limit = validateLimit(limit);

            // 1. 未登录用户 - 冷启动
            if (userId == null) {
                log.info("用户未登录，使用冷启动推荐");
                return getColdStartRecommend(limit);
            }

            // 2. 检查是否为冷启动用户
            if (coldStartStrategy.isColdStartUser(userId)) {
                log.info("冷启动用户，使用冷启动策略: userId={}", userId);
                Set<Integer> readBookIds = getUserReadBookIds(userId);
                List<RecommendItemVo> recommendations = coldStartStrategy.getColdStartRecommendations(
                        limit, readBookIds);
                
                Map<String, Object> result = new HashMap<>();
                result.put("recommendations", recommendations);
                result.put("total", recommendations.size());
                result.put("algorithm", "cold_start");
                
                return ResponseResult.okResult(result);
            }

            log.info("获取个性化推荐: userId={}, limit={}", userId, limit);

            // 3. 获取用户画像
            Query query = new Query(Criteria.where("userId").is(userId));
            UserProfile profile = mongoTemplate.findOne(query, UserProfile.class);

            if (profile == null) {
                log.info("用户画像不存在，使用冷启动推荐: userId={}", userId);
                Set<Integer> readBookIds = getUserReadBookIds(userId);
                List<RecommendItemVo> recommendations = coldStartStrategy.getColdStartRecommendations(
                        limit, readBookIds);
                
                Map<String, Object> result = new HashMap<>();
                result.put("recommendations", recommendations);
                result.put("total", recommendations.size());
                result.put("algorithm", "cold_start");
                
                return ResponseResult.okResult(result);
            }

            // 4. 获取用户已读图书ID，用于过滤
            Set<Integer> readBookIds = getUserReadBookIds(userId);

            // 5. 基于用户画像推荐
            List<RecommendItemVo> recommendations = new ArrayList<>();

            // 5.1 基于分类偏好推荐
            if (profile.getFavoriteCategory() != null) {
                List<RecommendItemVo> categoryBooks = getBooksByCategory(
                        profile.getFavoriteCategory(), limit, readBookIds);
                categoryBooks.forEach(book -> {
                    book.setRecommendReason("你喜欢" + profile.getFavoriteCategory() + "类书籍");
                    book.setAlgorithmType("category_based");
                });
                recommendations.addAll(categoryBooks);
            }

            // 5.2 基于协同过滤推荐
            List<RecommendItemVo> collaborativeBooks = getCollaborativeFilteringBooks(
                    userId, limit / 2, readBookIds);
            recommendations.addAll(collaborativeBooks);

            // 5.3 去重并限制数量
            recommendations = deduplicateAndLimit(recommendations, limit);

            // 5.4 如果推荐数量不足，补充冷启动推荐（包含热门和多样化内容）
            if (recommendations.size() < limit) {
                int needCount = limit - recommendations.size();
                Set<Integer> existingIds = recommendations.stream()
                        .map(RecommendItemVo::getBookId)
                        .collect(Collectors.toSet());
                existingIds.addAll(readBookIds);
                
                List<RecommendItemVo> supplementBooks = coldStartStrategy.getColdStartRecommendations(
                        needCount, existingIds);
                supplementBooks.forEach(book -> {
                    if (book.getRecommendReason() == null || book.getRecommendReason().isEmpty()) {
                        book.setRecommendReason("为你推荐");
                    }
                });
                recommendations.addAll(supplementBooks);
            }

            Map<String, Object> result = new HashMap<>();
            result.put("recommendations", recommendations);
            result.put("total", recommendations.size());
            result.put("algorithm", "personalized");

            log.info("个性化推荐成功: userId={}, count={}", userId, recommendations.size());
            return ResponseResult.okResult(result);

        } catch (Exception e) {
            log.error("获取个性化推荐失败", e);
            return ResponseResult.errorResult(500, "获取推荐失败");
        }
    }

    @Override
    public ResponseResult getCollaborativeRecommend(Integer limit) {
        try {
            User user = MpThreadLocalUtil.getUser();
            Integer userId = user != null ? user.getId() : null;
            if (userId == null) {
                return getPopularRecommend(limit);
            }

            limit = validateLimit(limit);
            log.info("获取协同过滤推荐: userId={}, limit={}", userId, limit);

            Set<Integer> readBookIds = getUserReadBookIds(userId);
            List<RecommendItemVo> recommendations = getCollaborativeFilteringBooks(
                    userId, limit, readBookIds);

            // 如果推荐不足，补充热门图书
            if (recommendations.size() < limit) {
                List<RecommendItemVo> popularBooks = getPopularBooks(
                        limit - recommendations.size(), readBookIds);
                recommendations.addAll(popularBooks);
            }

            Map<String, Object> result = new HashMap<>();
            result.put("recommendations", recommendations);
            result.put("total", recommendations.size());
            result.put("algorithm", "collaborative_filtering");

            return ResponseResult.okResult(result);

        } catch (Exception e) {
            log.error("获取协同过滤推荐失败", e);
            return ResponseResult.errorResult(500, "获取推荐失败");
        }
    }

    @Override
    public ResponseResult getPopularRecommend(Integer limit) {
        try {
            limit = validateLimit(limit);
            log.info("获取热门推荐: limit={}", limit);

            // 尝试从缓存获取（不区分用户的热门推荐可以共享）
            String cacheKey = "recommend:popular:" + limit;
            String cachedData = cacheService.get(cacheKey);
            if (cachedData != null) {
                log.debug("从缓存获取热门推荐");
                return JSON.parseObject(cachedData, ResponseResult.class);
            }

            User user = MpThreadLocalUtil.getUser();
            Integer userId = user != null ? user.getId() : null;
            Set<Integer> readBookIds = userId != null ? getUserReadBookIds(userId) : new HashSet<>();

            List<RecommendItemVo> recommendations = getPopularBooks(limit, readBookIds);

            Map<String, Object> result = new HashMap<>();
            result.put("recommendations", recommendations);
            result.put("total", recommendations.size());
            result.put("algorithm", "popularity");

            ResponseResult response = ResponseResult.okResult(result);
            
            // 缓存结果
            cacheService.setEx(cacheKey, JSON.toJSONString(response), CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);

            return response;

        } catch (Exception e) {
            log.error("获取热门推荐失败", e);
            return ResponseResult.errorResult(500, "获取推荐失败");
        }
    }

    @Override
    public ResponseResult getCategoryRecommend(Integer categoryId, Integer limit) {
        try {
            limit = validateLimit(limit);
            log.info("获取分类推荐: categoryId={}, limit={}", categoryId, limit);

            // 直接调用BookService，不再使用Feign
            ResponseResult result = bookService.getBooksByCategory(categoryId, 1, limit);
            if (result == null || result.getCode() != 200) {
                log.warn("获取分类图书失败: categoryId={}", categoryId);
                return getPopularRecommend(limit);
            }

            // 转换为推荐格式
            List<RecommendItemVo> recommendations = convertBooksToRecommendations(
                    result.getData(), "基于分类推荐", "category");

            Map<String, Object> resultData = new HashMap<>();
            resultData.put("recommendations", recommendations);
            resultData.put("total", recommendations.size());
            resultData.put("algorithm", "category");

            return ResponseResult.okResult(resultData);

        } catch (Exception e) {
            log.error("获取分类推荐失败: categoryId={}", categoryId, e);
            return ResponseResult.errorResult(500, "获取推荐失败");
        }
    }

    @Override
    public ResponseResult getSimilarBooksRecommend(Integer bookId, Integer limit) {
        try {
            limit = validateLimit(limit);
            log.info("获取相似图书推荐: bookId={}, limit={}", bookId, limit);

            // 1. 获取目标图书信息
            ResponseResult bookResult = bookService.getBookVo(bookId);
            if (bookResult == null || bookResult.getCode() != 200 || bookResult.getData() == null) {
                log.warn("目标图书不存在: bookId={}", bookId);
                return ResponseResult.errorResult(404, "图书不存在");
            }

            JSONObject targetBook = JSON.parseObject(JSON.toJSONString(bookResult.getData()));
            Integer categoryId = targetBook.getInteger("categoryId");

            // 2. 获取同分类的图书
            if (categoryId != null) {
                ResponseResult categoryResult = bookService.getBooksByCategory(categoryId, 1, limit + 5);
                if (categoryResult != null && categoryResult.getCode() == 200) {
                    List<RecommendItemVo> recommendations = convertBooksToRecommendations(
                            categoryResult.getData(),
                            "与《" + targetBook.getString("title") + "》相似",
                            "similar");

                    // 过滤掉目标图书本身
                    recommendations = recommendations.stream()
                            .filter(r -> !r.getBookId().equals(bookId))
                            .limit(limit)
                            .collect(Collectors.toList());

                    Map<String, Object> result = new HashMap<>();
                    result.put("recommendations", recommendations);
                    result.put("total", recommendations.size());
                    result.put("algorithm", "similar");

                    return ResponseResult.okResult(result);
                }
            }

            // 如果没有同分类图书，返回热门推荐
            return getPopularRecommend(limit);

        } catch (Exception e) {
            log.error("获取相似图书推荐失败: bookId={}", bookId, e);
            return ResponseResult.errorResult(500, "获取推荐失败");
        }
    }

    @Override
    public ResponseResult getHybridRecommend(Integer limit) {
        try {
            User user = MpThreadLocalUtil.getUser();
            Integer userId = user != null ? user.getId() : null;
            if (userId == null) {
                return getPopularRecommend(limit);
            }

            limit = validateLimit(limit);
            log.info("获取混合推荐（猜你喜欢）: userId={}, limit={}", userId, limit);

            // 检查缓存
            String cacheKey = "recommend:hybrid:" + userId + ":" + limit;
            String cachedData = cacheService.get(cacheKey);
            if (cachedData != null) {
                log.debug("从缓存获取混合推荐: userId={}", userId);
                return JSON.parseObject(cachedData, ResponseResult.class);
            }

            Set<Integer> readBookIds = getUserReadBookIds(userId);
            List<RecommendItemVo> recommendations = new ArrayList<>();

            // 1. 个性化推荐 (40%)
            try {
                ResponseResult personalizedResult = getPersonalizedRecommend((int) (limit * 0.4));
                if (personalizedResult.getCode() == 200) {
                    Map<String, Object> data = (Map<String, Object>) personalizedResult.getData();
                    List<RecommendItemVo> personalizedBooks = (List<RecommendItemVo>) data.get("recommendations");
                    if (personalizedBooks != null) {
                        recommendations.addAll(personalizedBooks);
                    }
                }
            } catch (Exception e) {
                log.warn("获取个性化推荐失败，跳过", e);
            }

            // 2. 协同过滤推荐 (30%)
            try {
                List<RecommendItemVo> collaborativeBooks = getCollaborativeFilteringBooks(
                        userId, (int) (limit * 0.3), readBookIds);
                if (collaborativeBooks != null) {
                    recommendations.addAll(collaborativeBooks);
                }
            } catch (Exception e) {
                log.warn("获取协同过滤推荐失败，跳过", e);
            }

            // 3. 热门推荐 (30%)
            try {
                List<RecommendItemVo> popularBooks = getPopularBooks(
                        (int) (limit * 0.3), readBookIds);
                recommendations.addAll(popularBooks);
            } catch (Exception e) {
                log.warn("获取热门推荐失败，跳过", e);
            }

            // 4. 去重并限制数量（考虑分类多样性）
            recommendations = deduplicateAndLimit(recommendations, limit * 2); // 先取2倍，便于多样性过滤
            
            // 5. 增强多样性（分类交错排列）
            recommendations = enhanceDiversity(recommendations);
            
            // 6. 最终限制数量
            if (recommendations.size() > limit) {
                recommendations = recommendations.subList(0, limit);
            }

            Map<String, Object> result = new HashMap<>();
            result.put("recommendations", recommendations);
            result.put("total", recommendations.size());
            result.put("algorithm", "hybrid");

            ResponseResult response = ResponseResult.okResult(result);
            
            // 缓存结果（缓存时间短一些，增加多样性）
            cacheService.setEx(cacheKey, JSON.toJSONString(response), 180, TimeUnit.SECONDS); // 3分钟

            log.info("混合推荐成功: userId={}, count={}", userId, recommendations.size());
            return response;

        } catch (Exception e) {
            log.error("获取混合推荐失败", e);
            return ResponseResult.errorResult(500, "获取推荐失败");
        }
    }

    @Override
    public ResponseResult getNewBooksRecommend(Integer limit) {
        try {
            limit = validateLimit(limit);
            log.info("获取新书推荐: limit={}", limit);

            // 检查缓存
            String cacheKey = "recommend:new:" + limit;
            String cachedData = cacheService.get(cacheKey);
            if (cachedData != null) {
                log.debug("从缓存获取新书推荐");
                return JSON.parseObject(cachedData, ResponseResult.class);
            }

            // 直接调用BookService，不再使用Feign
            ResponseResult result = bookService.getNewBooks(1, limit);
            if (result == null || result.getCode() != 200) {
                log.warn("获取新书列表失败");
                return getPopularRecommend(limit);
            }

            List<RecommendItemVo> recommendations = convertBooksToRecommendations(
                    result.getData(), "新书推荐", "new");

            Map<String, Object> resultData = new HashMap<>();
            resultData.put("recommendations", recommendations);
            resultData.put("total", recommendations.size());
            resultData.put("algorithm", "new");

            ResponseResult response = ResponseResult.okResult(resultData);
            
            // 缓存结果
            cacheService.setEx(cacheKey, JSON.toJSONString(response), CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);

            return response;

        } catch (Exception e) {
            log.error("获取新书推荐失败", e);
            return ResponseResult.errorResult(500, "获取推荐失败");
        }
    }

    @Override
    public ResponseResult getRecommend(RecommendQueryDto dto) {
        try {
            String type = dto.getType();
            Integer limit = dto.getLimit();

            if (type == null || type.isEmpty()) {
                type = "personalized";
            }

            log.info("通用推荐接口: type={}, limit={}", type, limit);

            switch (type) {
                // 个性化推荐
                case "personalized":
                case "recommend":
                    return getPersonalizedRecommend(limit);
                
                // 协同过滤
                case "collaborative":
                    return getCollaborativeRecommend(limit);
                
                // 热门推荐
                case "popular":
                case "hot":
                case "premium":
                case "featured":        // 重磅推荐 - 使用热门
                case "bestseller":      // 畅销图书 - 使用热门
                    return getPopularRecommend(limit);
                
                // 分类推荐
                case "category":
                    return getCategoryRecommend(dto.getCategoryId(), limit);
                case "poetry":          // 电子诗集 - 诗歌分类
                    return getCategoryRecommendByName("诗歌", limit);
                case "ai":              // AI专区 - AI分类
                    return getCategoryRecommendByName("计算机", limit);
                
                // 相似推荐
                case "similar":
                    return getSimilarBooksRecommend(dto.getBookId(), limit);
                
                // 混合推荐
                case "guess":
                case "hybrid":
                    return getHybridRecommend(limit);
                
                // 新书推荐
                case "discovery":
                case "new":
                case "newRelease":      // 最新上架
                    return getNewBooksRecommend(limit);
                
                // 高评分推荐
                case "highRating":      // 口碑力荐 - 使用热门（评分高的通常也热门）
                    return getPopularRecommend(limit);
                
                // 出版社精选
                case "publisherChoice": // 出版社精选 - 使用热门
                    return getPopularRecommend(limit);
                
                default:
                    log.warn("不支持的推荐类型: {}, 使用个性化推荐", type);
                    return getPersonalizedRecommend(limit);
            }

        } catch (Exception e) {
            log.error("获取推荐失败", e);
            return ResponseResult.errorResult(500, "获取推荐失败");
        }
    }
    
    /**
     * 根据分类名称获取推荐
     */
    private ResponseResult getCategoryRecommendByName(String categoryName, Integer limit) {
        try {
            limit = validateLimit(limit);
            log.info("根据分类名称获取推荐: categoryName={}, limit={}", categoryName, limit);
            
            // TODO: 实现根据分类名称查询，目前降级为热门推荐
            // 可以调用book-service的分类查询接口
            log.warn("分类名称查询暂未实现，使用热门推荐: categoryName={}", categoryName);
            return getPopularRecommend(limit);
            
        } catch (Exception e) {
            log.error("根据分类名称获取推荐失败: categoryName={}", categoryName, e);
            return getPopularRecommend(limit);
        }
    }

    // ==================== 私有辅助方法 ====================

    /**
     * 冷启动推荐（未登录或新用户）
     */
    private ResponseResult getColdStartRecommend(Integer limit) {
        try {
            log.info("执行冷启动推荐: limit={}", limit);
            
            List<RecommendItemVo> recommendations = coldStartStrategy.getColdStartRecommendations(
                    limit, new HashSet<>());
            
            Map<String, Object> result = new HashMap<>();
            result.put("recommendations", recommendations);
            result.put("total", recommendations.size());
            result.put("algorithm", "cold_start");
            
            return ResponseResult.okResult(result);
            
        } catch (Exception e) {
            log.error("冷启动推荐失败", e);
            // 降级为热门推荐
            return getPopularRecommend(limit);
        }
    }

    /**
     * 验证并规范化limit参数
     */
    private Integer validateLimit(Integer limit) {
        if (limit == null || limit <= 0) {
            return DEFAULT_LIMIT;
        }
        return Math.min(limit, MAX_LIMIT);
    }

    /**
     * 获取用户已读图书ID集合
     */
    private Set<Integer> getUserReadBookIds(Integer userId) {
        try {
            Query query = new Query(Criteria.where("userId").is(userId)
                    .and("targetType").is("book")
                    .and("behaviorType").in(Arrays.asList("reading", "reading_start", "reading_end")));

            List<UserBehavior> behaviors = mongoTemplate.find(query, UserBehavior.class);

            return behaviors.stream()
                    .map(UserBehavior::getTargetId)
                    .filter(Objects::nonNull)
                    .map(id -> {
                        try {
                            return Integer.parseInt(id);
                        } catch (NumberFormatException e) {
                            return null;
                        }
                    })
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

        } catch (Exception e) {
            log.error("获取用户已读图书失败: userId={}", userId, e);
            return new HashSet<>();
        }
    }

    /**
     * 基于分类获取图书推荐
     */
    private List<RecommendItemVo> getBooksByCategory(String categoryName, Integer limit, Set<Integer> excludeIds) {
        try {
            // TODO: 需要book-service提供根据分类名称查询的接口
            // 目前返回空列表
            return new ArrayList<>();
        } catch (Exception e) {
            log.error("基于分类获取图书失败: category={}", categoryName, e);
            return new ArrayList<>();
        }
    }

    /**
     * 协同过滤推荐（增强版：增加分类多样性）
     * 基于相似用户的阅读行为推荐
     */
    private List<RecommendItemVo> getCollaborativeFilteringBooks(
            Integer userId, Integer limit, Set<Integer> excludeIds) {
        try {
            // 1. 找出相似用户（有相似阅读行为的用户）
            Set<Integer> similarUserIds = findSimilarUsers(userId, 10);

            if (similarUserIds.isEmpty()) {
                return new ArrayList<>();
            }

            // 2. 获取相似用户阅读过的图书
            Query query = new Query(Criteria.where("userId").in(similarUserIds)
                    .and("targetType").is("book")
                    .and("behaviorType").in(Arrays.asList("reading", "collect")));

            List<UserBehavior> behaviors = mongoTemplate.find(query, UserBehavior.class);

            // 3. 统计图书被阅读次数（作为推荐分数）
            Map<Integer, Long> bookScores = behaviors.stream()
                    .map(UserBehavior::getTargetId)
                    .filter(Objects::nonNull)
                    .map(id -> {
                        try {
                            return Integer.parseInt(id);
                        } catch (NumberFormatException e) {
                            return null;
                        }
                    })
                    .filter(Objects::nonNull)
                    .filter(id -> !excludeIds.contains(id))
                    .collect(Collectors.groupingBy(id -> id, Collectors.counting()));

            // 4. 按分数排序，取更多候选（用于多样性过滤）
            List<Integer> candidateBookIds = bookScores.entrySet().stream()
                    .sorted(Map.Entry.<Integer, Long>comparingByValue().reversed())
                    .limit(limit * 3) // 取3倍数量作为候选
                    .map(Map.Entry::getKey)
                    .collect(Collectors.toList());

            // 5. 获取图书详情并应用分类多样性过滤
            List<RecommendItemVo> recommendations = new ArrayList<>();
            Map<String, Integer> categoryCount = new HashMap<>();
            
            for (Integer bookId : candidateBookIds) {
                if (recommendations.size() >= limit) {
                    break;
                }
                
                try {
                    // 直接调用BookService，不再使用Feign
                    ResponseResult result = bookService.getBookVo(bookId);
                    if (result != null && result.getCode() == 200 && result.getData() != null) {
                        JSONObject bookInfo = JSON.parseObject(JSON.toJSONString(result.getData()));
                        
                        // 获取分类信息
                        String category = bookInfo.getString("category");
                        if (category == null || category.isEmpty()) {
                            category = bookInfo.getString("categoryName");
                        }
                        if (category == null) {
                            category = "未分类";
                        }
                        
                        // 应用分类多样性限制
                        int catCount = categoryCount.getOrDefault(category, 0);
                        if (catCount >= MAX_SAME_CATEGORY_COUNT) {
                            continue; // 该分类已达上限，跳过
                        }
                        
                        RecommendItemVo item = buildRecommendItem(
                                bookInfo,
                                bookScores.get(bookId).doubleValue(),
                                "喜欢相似书籍的用户也喜欢",
                                "collaborative");
                        recommendations.add(item);
                        categoryCount.put(category, catCount + 1);
                    }
                } catch (Exception e) {
                    log.warn("获取图书信息失败: bookId={}", bookId, e);
                }
            }

            return recommendations;

        } catch (Exception e) {
            log.error("协同过滤推荐失败: userId={}", userId, e);
            return new ArrayList<>();
        }
    }

    /**
     * 找出相似用户
     */
    private Set<Integer> findSimilarUsers(Integer userId, int limit) {
        try {
            // 1. 获取当前用户的阅读行为
            Query userQuery = new Query(Criteria.where("userId").is(userId)
                    .and("targetType").is("book"));
            List<UserBehavior> userBehaviors = mongoTemplate.find(userQuery, UserBehavior.class);

            Set<String> userBookIds = userBehaviors.stream()
                    .map(UserBehavior::getTargetId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

            if (userBookIds.isEmpty()) {
                return new HashSet<>();
            }

            // 2. 找出阅读过相同图书的其他用户
            Query similarQuery = new Query(Criteria.where("targetType").is("book")
                    .and("targetId").in(userBookIds)
                    .and("userId").ne(userId));

            List<UserBehavior> similarBehaviors = mongoTemplate.find(similarQuery, UserBehavior.class);

            // 3. 统计重叠图书数量，作为相似度
            Map<Integer, Long> userSimilarity = similarBehaviors.stream()
                    .map(UserBehavior::getUserId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.groupingBy(id -> id, Collectors.counting()));

            // 4. 返回相似度最高的用户
            return userSimilarity.entrySet().stream()
                    .sorted(Map.Entry.<Integer, Long>comparingByValue().reversed())
                    .limit(limit)
                    .map(Map.Entry::getKey)
                    .collect(Collectors.toSet());

        } catch (Exception e) {
            log.error("查找相似用户失败: userId={}", userId, e);
            return new HashSet<>();
        }
    }

    /**
     * 获取热门图书（增强版：增加多样性和随机性）
     */
    private List<RecommendItemVo> getPopularBooks(Integer limit, Set<Integer> excludeIds) {
        try {
            // 从Redis获取热度排行榜（取更多数据以支持多样性过滤）
            int fetchSize = Math.min((limit + excludeIds.size()) * 3, 100);
            Set<String> topBookIds = cacheService.zReverseRange(
                    KafkaConfig.Keys.BOOK_HOT_RANKING, 0, fetchSize);

            if (topBookIds == null || topBookIds.isEmpty()) {
                log.warn("热度排行榜为空");
                return new ArrayList<>();
            }

            List<RecommendItemVo> candidates = new ArrayList<>();
            Map<String, Integer> categoryCount = new HashMap<>();

            for (String bookIdStr : topBookIds) {
                try {
                    Integer bookId = Integer.parseInt(bookIdStr);
                    if (excludeIds.contains(bookId)) {
                        continue;
                    }

                    // 直接调用BookService，不再使用Feign
                    ResponseResult result = bookService.getBookVo(bookId);
                    if (result != null && result.getCode() == 200 && result.getData() != null) {
                        JSONObject bookInfo = JSON.parseObject(JSON.toJSONString(result.getData()));
                        Double score = cacheService.zScore(KafkaConfig.Keys.BOOK_HOT_RANKING, bookIdStr);
                        
                        String category = bookInfo.getString("category");
                        if (category == null || category.isEmpty()) {
                            category = bookInfo.getString("categoryName");
                        }
                        if (category == null) {
                            category = "未分类";
                        }

                        // 应用分类多样性限制
                        int catCount = categoryCount.getOrDefault(category, 0);
                        if (catCount >= MAX_SAME_CATEGORY_COUNT) {
                            continue; // 该分类已达上限，跳过
                        }

                        RecommendItemVo item = buildRecommendItem(
                                bookInfo,
                                score != null ? score : 0.0,
                                "热门好书推荐",
                                "popular");
                        candidates.add(item);
                        categoryCount.put(category, catCount + 1);

                        if (candidates.size() >= limit) {
                            break;
                        }
                    }
                } catch (NumberFormatException e) {
                    log.debug("跳过无效的bookId: {}", bookIdStr);
                }
            }

            // 轻微打乱顺序，增加每次推荐的新鲜感（但保持大致的热度排序）
            if (candidates.size() > 3) {
                shuffleWithinGroups(candidates, 3);
            }

            return candidates;

        } catch (Exception e) {
            log.error("获取热门图书失败", e);
            return new ArrayList<>();
        }
    }
    
    /**
     * 分组打乱：在小组内打乱顺序，保持整体趋势
     */
    private void shuffleWithinGroups(List<RecommendItemVo> list, int groupSize) {
        Random random = new Random();
        for (int i = 0; i < list.size(); i += groupSize) {
            int end = Math.min(i + groupSize, list.size());
            List<RecommendItemVo> subList = list.subList(i, end);
            Collections.shuffle(subList, random);
        }
    }

    /**
     * 构建推荐项对象
     */
    private RecommendItemVo buildRecommendItem(JSONObject bookInfo, Double score,
                                               String reason, String algorithm) {
        // 尝试多个可能的分类字段名
        String category = bookInfo.getString("categoryName");
        if (category == null || category.isEmpty()) {
            category = bookInfo.getString("category");
        }
        if (category == null || category.isEmpty()) {
            category = "未分类";
        }
        
        return RecommendItemVo.builder()
                .bookId(bookInfo.getInteger("id"))
                .bookTitle(bookInfo.getString("title"))
                .bookAuthor(bookInfo.getString("author"))
                .bookCover(bookInfo.getString("cover"))
                .bookCategory(category)
                .recommendScore(score)
                .recommendReason(reason)
                .algorithmType(algorithm)
                .build();
    }

    /**
     * 转换图书列表为推荐列表
     */
    private List<RecommendItemVo> convertBooksToRecommendations(
            Object data, String reason, String algorithm) {
        try {
            JSONObject jsonData = JSON.parseObject(JSON.toJSONString(data));
            List<JSONObject> books = jsonData.getJSONArray("records") != null ?
                    jsonData.getJSONArray("records").toJavaList(JSONObject.class) :
                    new ArrayList<>();

            return books.stream()
                    .map(book -> buildRecommendItem(book, 0.0, reason, algorithm))
                    .collect(Collectors.toList());

        } catch (Exception e) {
            log.error("转换推荐数据失败", e);
            return new ArrayList<>();
        }
    }

    /**
     * 去重并限制数量（增强版：考虑分类多样性）
     */
    private List<RecommendItemVo> deduplicateAndLimit(List<RecommendItemVo> items, int limit) {
        if (items == null || items.isEmpty()) {
            return new ArrayList<>();
        }
        
        // 1. 先按bookId去重
        Map<Integer, RecommendItemVo> uniqueItems = new LinkedHashMap<>();
        for (RecommendItemVo item : items) {
            uniqueItems.putIfAbsent(item.getBookId(), item);
        }
        
        // 2. 应用分类多样性过滤
        List<RecommendItemVo> result = new ArrayList<>();
        Map<String, Integer> categoryCount = new HashMap<>();
        
        for (RecommendItemVo item : uniqueItems.values()) {
            if (result.size() >= limit) {
                break;
            }
            
            String category = item.getBookCategory();
            if (category == null || category.isEmpty()) {
                category = "未分类";
            }
            
            int count = categoryCount.getOrDefault(category, 0);
            
            // 如果该分类的图书数量未达到上限，添加该图书
            if (count < MAX_SAME_CATEGORY_COUNT) {
                result.add(item);
                categoryCount.put(category, count + 1);
            }
        }
        
        // 3. 如果结果数量不足，放宽分类限制补充
        if (result.size() < limit) {
            Set<Integer> addedBookIds = result.stream()
                    .map(RecommendItemVo::getBookId)
                    .collect(Collectors.toSet());
            
            for (RecommendItemVo item : uniqueItems.values()) {
                if (result.size() >= limit) {
                    break;
                }
                if (!addedBookIds.contains(item.getBookId())) {
                    result.add(item);
                }
            }
        }
        
        return result;
    }
    
    /**
     * 增强多样性：打乱顺序并确保分类分布均匀
     */
    private List<RecommendItemVo> enhanceDiversity(List<RecommendItemVo> items) {
        if (items == null || items.size() <= 1) {
            return items;
        }
        
        // 按分类分组
        Map<String, List<RecommendItemVo>> categoryGroups = items.stream()
                .collect(Collectors.groupingBy(
                        item -> item.getBookCategory() != null ? item.getBookCategory() : "未分类"
                ));
        
        // 轮流从每个分类中取一本，实现交错排列
        List<RecommendItemVo> result = new ArrayList<>();
        boolean hasMore = true;
        int round = 0;
        
        while (hasMore && result.size() < items.size()) {
            hasMore = false;
            for (List<RecommendItemVo> group : categoryGroups.values()) {
                if (round < group.size()) {
                    result.add(group.get(round));
                    hasMore = true;
                }
            }
            round++;
        }
        
        return result;
    }
}

